10203. Арктическая
сеть
Министерство
национальной обороны (МНО) планирует соединить несколько северных форпостов в
единую беспроводную сеть. Для построения сети должны использоваться две
различные технологии связи: каждая застава будет оснащена радиоприёмником, а
некоторые из них дополнительно получат спутниковый канал.
Любые
два аванпоста, имеющие спутниковый канал, могут связываться между собой через
спутник, независимо от расстояния между ними. В противном случае два аванпоста
могут обмениваться данными по радио только в том случае, если расстояние между
ними не превышает d, зависящее
от мощности трансиверов. Чем выше мощность, тем больше значение d, однако стоимость оборудования при
этом также возрастает. По экономическим и эксплуатационным соображениям все
трансиверы должны быть одинаковыми, то есть значение d должно быть одинаковым для всех пар форпостов.
Определите
минимальное значение d, при
котором обеспечивается связь между любыми двумя аванпостами – напрямую или
через другие заставы (по цепочке соединений).
Вход. Первая
строка содержит количество тестов n.
В первой строке каждого теста указаны количество спутниковых каналов s (1 ≤ s ≤ 100) и количество аванпостов p (s
< p ≤ 500). Далее
следуют p строк, каждая из
которых содержит координаты (x, y) соответствующей заставы в километрах
(целые числа от 0 до 10000).
Выход. Для
каждого теста выведите минимальное значение d, необходимое для соединения всех аванпостов в сеть. Результат
следует вывести с точностью до двух десятичных знаков.
Пример
входа |
Пример
выхода |
1 2 4 1 0 3 0 6 0 7 2 |
2.24 |
графы –
алгоритм Прима
Запустим алгоритм
Прима. Построим массив dist, в котором dist[i] хранит длину кратчайшего
ребра из минимального остова, входящего в вершину i. То есть как
раз из этих ребер и состоит минимальный остов. При этом dist[0] = 0, так
как стартуем алгоритм из вершины 0.
По окончанию
алгоритма Прима отсортируем массив dist. Спутниковыми каналами следует
соединить наиболее отдаленные аванпосты. Их следует разместить в тех s
аванпостах, которые соединяют самые длинные ребра из dist (s – 1
ребро соединяет s аванпостов). Следовательно искомым значением d будет
длина ребра, являющегося s-ым с конца dist.
Пример
Рассмотрим
пример, приведенный в условии.
Два спутниковых
канала ставим в точках (3; 0) и (6; 0). Таким образом
заставы в этих координатах связываются через спутник. Среди оставшихся ребер
минимального остовного дерева ищем наибольшее. Таким будет ребро, соединяющее
точки (6; 0) и (7; 2), его длина
равна 2.24.
Реализация алгоритма
Объявим глобальные массивы. Координаты застав храним в (x[i], y[i]).
Значение used[i] = 1, если вершина i включена в остов.
#define MAX 501
#define INF
0x3F3F3F3F
int x[MAX], y[MAX];
int used[MAX],
dist[MAX];
Функция dist2 вычисляет квадрат расстояния между заставами i и j.
int dist2(int i, int j)
{
return (x[j] -
x[i])*(x[j] - x[i]) +
(y[j] - y[i])*(y[j] -
y[i]);
}
Функция Prim реализует алгоритм Прима.
void Prim(void)
{
Инициализируем массивы.
memset(dist,
0x3F, sizeof(dist));
memset(used,
0, sizeof(used));
Начинаем строить минимальный остов с вершины 0. Инициализируем dist[0] = 0, used[0] = 1.
int i,
j, cur = 0;
dist[cur]
= 0;
used[cur]
= 1;
Совершаем n – 1 итерацию. На каждой итерации в остов
добавляем одну вершину (так что в остов еще следует добавить n
– 1
вершин).
for (i =
1; i < n; i++)
{
Текущей вершиной является cur. Перебираем ребра (cur,
j) и пересчитываем значение dist[j]. Таким образом dist[j] хранит текущее кратчайшее расстояние от
вершины j до текущего минимального остова.
for (j =
0; j < n; j++)
if
(!used[j] && (dist2(cur, j) < dist[j]))
dist[j] = dist2(cur, j);
Ищем ребро наименьшей длины, которое будет включено в остов. Для этого ищем
такое наименьшее dist[j], что вершина j еще не принадлежит
остову (used[j] = 0). Номер вершины с наименьшим dist[j] заносим в cur
(она
становится текущей).
int min
= INF;
for (j =
0; j < n; j++)
if
(!used[j] && (dist[j] < min))
{
min = dist[j];
cur = j;
}
Вершина cur включается в остов.
used[cur]
= 1;
}
}
Основная часть программы. Обрабатываем tests тестов.
scanf("%d", &tests);
while (tests--)
{
Читаем входные данные.
scanf("%d
%d", &s, &n);
for (i =
0; i < n; i++)
scanf("%d
%d", &x[i], &y[i]);
Запускаем алгоритм Прима.
Prim();
Сортируем массив dist.
sort(dist,
dist + n);
Выводим ответ.
printf("%.2lf\n",
sqrt(dist[n - s]));
}
Java реализация
import java.util.*;
public class Main
{
static int n;
static int x[], y[];
static int used[], dist[];
static int
dist2(int i, int j)
{
return (x[j] - x[i])*(x[j] - x[i]) +
(y[j] - y[i])*(y[j] - y[i]);
}
static void
Prim()
{
dist = new int[n];
Arrays.fill(dist,
Integer.MAX_VALUE);
used = new int[n];
int i, j, cur = 0;
dist[cur] =
0;
used[cur] =
1;
for (i = 1;
i < n; i++)
{
for (j = 0;
j < n; j++)
if (used[j] ==
0 && dist2(cur, j)
< dist[j])
dist[j] = dist2(cur, j);
int min =
Integer.MAX_VALUE;
for (j = 0;
j < n; j++)
if (used[j] ==
0 && dist[j]
< min)
{
min = dist[j];
cur = j;
}
used[cur] =
1;
}
}
public static void
main(String[] args)
{
Scanner con = new
Scanner(System.in);
int tests = con.nextInt();
while (tests--
> 0)
{
int s = con.nextInt();
n = con.nextInt();
x = new int[n];
y = new int[n];
for (int i = 0;
i < n; i++)
{
x[i] = con.nextInt();
y[i] = con.nextInt();
}
Prim();
Arrays.sort(dist);
System.out.println(Math.sqrt(dist[n - s]));
}
con.close();
}
}